package com.zzyl.job;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.config.WebSocketServer;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.entity.DeviceData;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRoleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.service.AlertRoleService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AlertRole {
    @Autowired
    AlertRoleService alertRoleService;
    @Autowired
    AlertRoleMapper alertRoleMapper;
    @Autowired
    DeviceMapper deviceMapper;
    @Autowired
    UserRoleMapper userRoleMapper;
    @Autowired
    AlertDataMapper alertDataMapper;

    @Autowired
    RedisTemplate<String,String> redisTemplate;
    @Scheduled(cron="0 * * * * ?")
    public void deviceDataAlert(){
        //查询所有规则 为空程序结束
        List<AlertRule> allAlertRule=alertRoleMapper.getAllAlertByThree(null,null,null);
        if(ObjectUtil.isEmpty(allAlertRule)){
            //没有规则 结束运行
            return;
        }
        //查询所有设备上报的数据 每个list其实是DeviceDatVo
        List<Object> redisData = redisTemplate.opsForHash().values("deviceData");
        //为空程序结束
        if(ObjectUtil.isEmpty(redisData)){
            return;
        }

        //将所有数据转成DeviceDataVo类型
        List<DeviceDataVo> deviceDataVos=new ArrayList<>();
        redisData.forEach(redisdata->{
            List<DeviceDataVo> list = JSONUtil.toList(redisdata.toString(), DeviceDataVo.class);
            deviceDataVos.addAll(list);

        });

        //所有的deviceDataVos，也就是所有的数据
        deviceDataVos.forEach(deviceDataVo -> {
            //我们要对每一个数据都就行判断
            alertFilter(deviceDataVo);
        });
    }

    private void alertFilter(DeviceDataVo deviceDataVo) {
        //获取设备上报数据时间，如果上报发生在1分钟前，为历史数据，不再处理
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        //时间不新鲜了 不在处理
        if(between>60){
//            return;
        }
        //遍历每一个和此设备一致，和此设备报警类型一致的 规则
        List<AlertRule> thisAlertRule=alertRoleMapper.getAllAlertByThree(deviceDataVo.getFunctionId(),deviceDataVo.getIotId(),deviceDataVo.getProductKey());
        //遍历所有此设备的规则 -1替换iotId
        List<AlertRule> allAlertRule=alertRoleMapper.getAllAlertByThree(deviceDataVo.getFunctionId(), "-1", deviceDataVo.getProductKey());
        //将两者的进行拼接
        Collection<AlertRule> alertRules = CollUtil.addAll(thisAlertRule, allAlertRule);
        //没有规则
        if(ObjectUtil.isEmpty(alertRules)){
            return;
        }
        //对此一项数据进行每一个规则的校验
        alertRules.forEach(alertRule -> {
            String iotId = deviceDataVo.getIotId();
            //从redis中获取数据，报警规则连续触发次数的key
            String alertCountKey = "AlertPeriodCount"+iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();
            //从redis中获取数据，沉默周期的key
            String sleepCountKey="AlertSleepCount"+iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();
            //1第一层校验 时间
            String[] split = alertRule.getAlertEffectivePeriod().split("~");
            LocalTime begin = LocalTime.parse(split[0]);
            LocalTime end=LocalTime.parse(split[1]);
            //把数据的年月日时分秒->时分秒
            LocalTime time = LocalDateTimeUtil.of(deviceDataVo.getAlarmTime()).toLocalTime();
            if(time.isBefore(begin)||time.isAfter(end)){
//                return;//不再时间内 跳出次循环
            }
            //2第二层校验 是否超过阈值(浮点比较 需要工具类)
            int compare = NumberUtil.compare(alertRule.getValue(), Double.valueOf(deviceDataVo.getDataValue()));
            //2.1超过/未达到阈值情况
            if(compare<=0&&">=".equals(alertRule.getOperator())||compare>0&&"<".equals(alertRule.getOperator())){
                log.debug("超过/未达到峰值 准备报警");
            }else{//不满足报警条件
//该情况不符合报警规则，所以设备上报数据为正常数据。需要删除redis聚合的异常数据,程序结束
//                redisTemplate.delete(alertCountKey);
//                return;
            }

            //3第三层校验：校验当前设备+功能的报警规则是否处于沉默周期
            Object o = redisTemplate.opsForValue().get(sleepCountKey);
            if(ObjectUtil.isNotEmpty(o)){//还在沉默周期内 无法报警跳出循环
                return;
            }

            //4第四层校验：校验持续周期
            Object o1=redisTemplate.opsForValue().get(alertCountKey);
            int n;
            if(ObjectUtil.isEmpty(o1)){//为空 第一次 设置为1后return
                redisTemplate.opsForValue().set(alertCountKey,"1");
                n=1;
            }else{//不为空 加一
                n=Integer.parseInt(redisTemplate.opsForValue().get(alertCountKey).toString())+1;
            }
            if(n<alertRule.getDuration()){//未到达报警的次数 把新的报警数存入 return
                redisTemplate.opsForValue().set(sleepCountKey,n+"");
                return;
            }
            //达到持续周期 开始报警
            //删除对应的报警次数
            redisTemplate.delete(alertCountKey);
            //设置沉默周期
            redisTemplate.opsForValue().set(sleepCountKey,"睡觉中",alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

            //将报警数据存入数据库 0：老人异常数据，1：设备异常数据',
            List<Long> consumerIds=new ArrayList<>();
            if(ObjectUtil.equals("0",alertRule.getAlertDataType())){
                //查询老人对应的护工 0：随身设备 1：固定设备',
                if(deviceDataVo.getLocationType()==0){
                    //通过老人查护理员id
                    consumerIds=deviceMapper.getNursingByElder(deviceDataVo.getIotId());
                }else{
                    //通过床位查询护理员id
                    consumerIds=deviceMapper.getNursingByElder(deviceDataVo.getIotId());
                }
            }else{//查询维修员id
                String roleName="维修工";
               consumerIds= userRoleMapper.getUserIdByJobName(roleName);
            }
            //查询超级管理员
            String superName="超级管理员";
            List<Long> fixManIds= userRoleMapper.getUserIdByJobName(superName);
            //变成所有消费者的id
            fixManIds.addAll(consumerIds);
            //去重
            ArrayList<Long> distinct = CollUtil.distinct(fixManIds);

            //代码太杂了 创建个新的方法 新增数据
            insertAlertData(distinct,alertRule,deviceDataVo);
        });

    }
    @Autowired
    WebSocketServer webSocketServer;

    private void insertAlertData(List<Long> fixManIds, AlertRule alertRule, DeviceDataVo deviceDataVo) {
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
        List<AlertData> list=new ArrayList<>();
        fixManIds.forEach(consumeId->{
            AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
//            alertData.setProcessorId(consumeId);
            alertData.setAlertReason(alertReason);
            alertData.setType(alertRule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(consumeId);
            alertData.setAlertRuleId(alertRule.getId());
            alertData.setIotId(deviceDataVo.getIotId());
            list.add(alertData);
        });

        alertDataMapper.insert(list);
        AlertNotifyDto bean = BeanUtil.toBean(deviceDataVo, AlertNotifyDto.class);
        bean.setFunctionName(alertRule.getFunctionName());
        bean.setAlertDataType(alertRule.getAlertDataType());
        bean.setNotifyType(1);

       webSocketServer.sendMessageToConsumer(bean,fixManIds);

    }
}
